﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Diagnostics;
using System.Net;
using System.Threading;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Xml;
using System.Xml.Linq;
using System.Configuration;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Queue;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Table;
using Microsoft.ServiceBus;
using Microsoft.ServiceBus.Messaging;
using System.Globalization;
using System.Linq;



namespace qsub
{
    class qsub
    {
        // Name of queues

        private const string IDLE = "idlequeue";
        //name of job table
        private const string TABLE = "jobtable";
        //name of command topic
        private const string CQNAME = "cqueue";
        //name of status topic
        private const string RQNAME = "outqueue";
        //name of service bus
        private const string SBNAME = "<service namespace>";
        //service bus key
        private const string SBKEY = "<service bus key>";
        //service bus owner
        private const string SBISSUER = "owner";

        // Tolerable wait time for tasks in queue
        private TimeSpan tolerable = TimeSpan.FromMinutes(1);
        // Max idle time before an instance signals that it is idle
        static TimeSpan _maxidletime = TimeSpan.FromMinutes(5);
        // Max duration of a job = visibility timeout to be set
        static TimeSpan _maxjobtime = TimeSpan.FromMinutes(5);

        // set these constants with your values to run the sample.
        private const string _serviceName = "geres3";
        private const string _storageacc = "<storage account name>";
        private const string _storagekey = "<storage key>";

        //Connection Strings
        private const string _sbconnectionstring = "Endpoint=sb://<service namespace>.servicebus.windows.net;SharedSecretIssuer=owner;SharedSecretValue=<service bus key>";
        private const string _connectionstring = "DefaultEndpointsProtocol=https;AccountName=<storage account name>;AccountKey=53A4/9CSO0HM53bwHe8Jpw8Nlo736onZtPPnhVHKLzbjupOMGbi12eFq1USzPUGEpK3jK+/UxdDoiZeRBExe8Q==";


        public class JobEntity : TableEntity
        {
            public JobEntity(string node, string jobid)
            {
                this.PartitionKey = node;
                this.RowKey = jobid;
            }
            public JobEntity() { }
            public string Status { get; set; }
            public DateTime TimeIn { get; set; }
            public DateTime TimeUpdated { get; set; }
        }

        static int Main(string[] args)
        {
            string qname = "";
            string command = "";
            string exclusive = "";
            if (args.Length != 3)
            {
                Console.WriteLine("Usage: qsub <queue name> <exclusive/notexclusive> \"<path\\command.exe parameter1 parameter2...>\"");
                return 1;
            }
            else
            {
                qname = args[0];
                exclusive = args[1];
                command = args[2];
            }


            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(_connectionstring);

            CloudQueueClient queueclient = storageAccount.CreateCloudQueueClient();
            CloudQueue taskqueue = queueclient.GetQueueReference(qname);
            if (!taskqueue.Exists())
            {
                Console.WriteLine("Task queue: {0} does not exist.", qname);
                return 1;
            }

            CloudQueue idlequeue = queueclient.GetQueueReference(IDLE);
            idlequeue.CreateIfNotExists();

            CloudTableClient tableclient = storageAccount.CreateCloudTableClient();
            CloudTable jobtable = tableclient.GetTableReference(TABLE);
            jobtable.CreateIfNotExists();

            var sbnamespace = NamespaceManager.CreateFromConnectionString(_sbconnectionstring);
            //create the command topic and the results topic if they do not exist.
            if (!sbnamespace.TopicExists(CQNAME)) sbnamespace.CreateTopic(CQNAME);
            if (!sbnamespace.TopicExists(RQNAME)) sbnamespace.CreateTopic(RQNAME);

            //connect to results topic
            TopicClient rqclient = TopicClient.CreateFromConnectionString(_sbconnectionstring, RQNAME);

            // node name
            string node = Dns.GetHostName();

            string jobid = RandomString();
            string qentry = jobid + " " + exclusive + " " + command;
            JobEntity tableentry = new JobEntity();
            tableentry.PartitionKey = node;
            tableentry.RowKey = jobid;
            tableentry.Status = "Submitted";
            tableentry.TimeIn = DateTime.Now;
            tableentry.TimeUpdated = DateTime.Now;
            TableOperation enter = TableOperation.InsertOrReplace(tableentry);
            BrokeredMessage msg = new BrokeredMessage("Submitted");
            msg.Properties["JobID"] = jobid;
            //put job in queue
            taskqueue.AddMessage(new CloudQueueMessage(qentry));
            //add job to table for tracking
            jobtable.Execute(enter);
            //send notification of job status
            rqclient.Send(msg);

            //everything ok
            Console.WriteLine("Job id: {0} submitted.", jobid);
            return 0;


        }

        static JobEntity jobquery(CloudTable jobtable, string jobid)
        {

            TableQuery<JobEntity> query = new TableQuery<JobEntity>().Where(TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.Equal, jobid));
            var results = jobtable.ExecuteQuery(query);
            //the crappy sdk return an ienumerable, not a list (inconsistent with other SDKs)!!!
            // so I cannot use count or last etc... to find the last item.
            // also foreach does not let us use its last item value!! who wrote this crap!!

            foreach (var item in results)
            {
                Console.WriteLine("{0:-10}\t{1:-16}\t{2:-10}\t{3:G}\t{4:G}", item.PartitionKey, item.RowKey, item.Status, item.TimeIn, item.TimeUpdated);
            }
            using (var iter = results.GetEnumerator())
            {
                if (iter.MoveNext())
                {
                    var last = iter.Current;
                    while (iter.MoveNext())
                    {
                        // here, "last" is a non-final value; do something with "last"
                        last = iter.Current;
                    }
                    // here, "last" is the FINAL one; do something else with "last"
                    return last;
                }
                else
                    return null;
            }
        }


        //generate random 16-character string
        static string RandomString()
        {
            var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            Random randomn = new Random();
            return new string(chars.Select(c => chars[randomn.Next(chars.Length)]).Take(16).ToArray());
        }
    }
}

